﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using System.Configuration;

using Infraestructure.Data.MainModule.UnitOfWork;
using Infraestructure.CrossCutting.IoC.Resources;
using Infraestructure.Data.MainModule.Mock;
using Infraestructure.CrossCutting.IoC.Unity.LifetimeManagers;
using Infraestructure.CrossCutting.NetFramework.Logging;
using Infraestructure.CrossCutting.Logging;
using Infraestructure.Data.MainModule.Repositories;
using Domain.MainModule.Addresses;
using Domain.MainModule.AddressTypes;
using Domain.MainModule.BankAccounts;
using Domain.MainModule.BillingInformations;
using Domain.MainModule.Branches;
using Domain.MainModule.Cities;
using Domain.MainModule.Countries;
using Domain.MainModule.CustomerCompanies;
using Domain.MainModule.Customers;
using Domain.MainModule.CustomersMTS;
using Domain.MainModule.Employees;
using Domain.MainModule.GroupBranches;
using Domain.MainModule.Common;
using Domain.MainModule.Identities;
using Domain.MainModule.IdentityTypes;
using Domain.MainModule.Phones;
using Domain.MainModule.PhoneTypes;
using Domain.MainModule.States;
using Domain.MainModule.VirtualAccounts;
using Domain.MainModule.CommissionsExclusiveAgentPayer;
using Domain.MainModule.CommissionsExclusiveBranch;
using Domain.MainModule.CommissionsExclusiveCountryPayer;
using Domain.MainModule.CommissionsExclusiveGroup;
using Domain.MainModule.CommissionsStandard;
using Domain.MainModule.SoundexCodes;
using Domain.MainModule.Orders;
using Domain.MainModule.CreditCards;
using Domain.MainModule.CustomerServices;
using Domain.MainModule.OrderDetails;
using Domain.MainModule.RateParamTypes;
using Domain.MainModule.Rates;
using Domain.MainModule.RetailRates;
using Domain.MainModule.GroupBranchCurrencies;
using Domain.MainModule.BankAccountTypes;
using Domain.MainModule.Banks;

using Application.MainModule.CustomersManagement;
using Application.MainModule.MoneyTransferManagement;
using Application.MainModule.AddressManagement;
using Application.MainModule.BankAccountsManagement;
using Application.MainModule.BranchesManagement;
using Application.MainModule.CommonManagement;
using Application.MainModule.CitiesManagement;
using Application.MainModule.CountriesManagement;
using Application.MainModule.GroupBranchesManagement;
using Application.MainModule.IdentitiesManagement;
using Application.MainModule.OrderManagement;
using Application.MainModule.PhonesManagement;
using Application.MainModule.SoundexCodesManagement;
using Application.MainModule.StatesManagement;
using Application.MainModule.VirtualAccountsManagement;
using Application.MainModule.CreditCardManagement;
using Application.MainModule.CustomerServiceManagement;
using Application.MainModule.GroupBranchCurrencyManagement;
using Application.MainModule.OrderDetailManagement;
using Application.MainModule.RateParamTypeManagement;
using Application.MainModule.RateManagement;
using Application.MainModule.RetailRateManagement;

using Infraestructure.CrossCutting.Auditing;
using Infraestructure.CrossCutting.Mailing;
using Infraestructure.CrossCutting.Caching;
using Infraestructure.CrossCutting.Security;
using Infraestructure.CrossCutting.FTP;
using Infraestructure.CrossCutting.NetFramework.Mailing;
using Infraestructure.CrossCutting.NetFramework.Caching;
using Infraestructure.CrossCutting.NetFramework.Security;
using Infraestructure.CrossCutting.NetFramework.FTP;
using Infraestructure.Data.Core;
using Application.MainModule.CustomerCompaniesManagement;

namespace Infraestructure.CrossCutting.IoC.Unity
{
    /// <summary>
    /// Implemented container in Microsoft Practices Unity
    /// </summary>
    sealed class IoCUnityContainer
        : IContainer
    {
        #region Members

        IDictionary<string, IUnityContainer> _ContainersDictionary;


        #endregion

        #region Constructor

        /// <summary>
        /// Create a new instance of IoCUnitContainer
        /// </summary>
        public IoCUnityContainer()
        {
            _ContainersDictionary = new Dictionary<string, IUnityContainer>();

            //Create root container
            IUnityContainer rootContainer = new UnityContainer();
            _ContainersDictionary.Add("RootContext", rootContainer);

            //Create container for real context, child of root container
            IUnityContainer realAppContainer = rootContainer.CreateChildContainer();
            _ContainersDictionary.Add("RealAppContext", realAppContainer);

            //Create container for testing, child of root container
            IUnityContainer fakeAppContainer = rootContainer.CreateChildContainer();
            _ContainersDictionary.Add("FakeAppContext", fakeAppContainer);


            ConfigureRootContainer(rootContainer);
            ConfigureRealContainer(realAppContainer);
            ConfigureFakeContainer(fakeAppContainer);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Configure root container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureRootContainer(IUnityContainer container)
        {
            // Take into account that Types and Mappings registration could be also done using the UNITY XML configuration
            //But we prefer doing it here (C# code) because we'll catch errors at compiling time instead execution time, 
            //if any type has been written wrong.

            //Register Repositories mappings
            
            container.RegisterType<IBankAccountRepository, BankAccountRepository>(new TransientLifetimeManager());
            container.RegisterType<ICustomerRepository, CustomerRepository>(new TransientLifetimeManager());
            container.RegisterType<ICountryRepository, CountryRepository>(new TransientLifetimeManager());
            container.RegisterType<IAddressRepository, AddressRepository>(new TransientLifetimeManager());
            container.RegisterType<IAddressTypeRepository, AddressTypeRepository>(new TransientLifetimeManager());
            container.RegisterType<IBillingInformationRepository, BillingInformationRepository>(new TransientLifetimeManager());
            container.RegisterType<IBranchRepository, BranchRepository>(new TransientLifetimeManager());
            container.RegisterType<ICityRepository, CityRepository>(new TransientLifetimeManager());
            container.RegisterType<ICommissionExclusiveAgentPayerRepository, CommissionExclusiveAgentPayerRepository>(new TransientLifetimeManager());
            container.RegisterType<ICommissionExclusiveBranchRepository, CommissionExclusiveBranchRepository>(new TransientLifetimeManager());
            container.RegisterType<ICommissionExclusiveCountryPayer, CommissionExclusiveCountryPayerRepository>(new TransientLifetimeManager());
            container.RegisterType<ICommissionExclusiveGroupRepository, CommissionExclusiveGroupRepository>(new TransientLifetimeManager());
            container.RegisterType<ICommissionStandardRepository, CommissionStandardRepository>(new TransientLifetimeManager());
            container.RegisterType<ICustomerCompanyRepository, CustomerCompanyRepository>(new TransientLifetimeManager());
            container.RegisterType<ICustomerMTSRepository, CustomerMTSRepository>(new TransientLifetimeManager());
            container.RegisterType<IEmployeeRepository, EmployeeRepository>(new TransientLifetimeManager());
            container.RegisterType<IGroupBranchRepository, GroupBranchRepository>(new TransientLifetimeManager());
            container.RegisterType<IGenderRepository, GenderRepository>(new TransientLifetimeManager());
            container.RegisterType<IIdentityRepository, IdentityRepository>(new TransientLifetimeManager());
            container.RegisterType<IIdentityTypeRepository, IdentityTypeRepository>(new TransientLifetimeManager());
            container.RegisterType<IPhoneRepository, PhoneRepository>(new TransientLifetimeManager());
            container.RegisterType<IPhoneTypeRepository, PhoneTypeRepository>(new TransientLifetimeManager());
            container.RegisterType<ISoundexCodeRepository, SoundexCodeRepository>(new TransientLifetimeManager());
            container.RegisterType<IStateRepository, StateRepository>(new TransientLifetimeManager());
            container.RegisterType<IVirtualAccountRepository, VirtualAccountRepository>(new TransientLifetimeManager());
            container.RegisterType<IMaritalStatusRepository, MaritalStatusRepository>(new TransientLifetimeManager());
            container.RegisterType<IOrderRepository, OrderRepository>(new TransientLifetimeManager());
            container.RegisterType<IStateRepository, StateRepository>(new TransientLifetimeManager());
            container.RegisterType<ICreditCardRepository, CreditCardRepository>(new TransientLifetimeManager());
            container.RegisterType<ICustomerServiceRepository, CustomerServiceRepository>(new TransientLifetimeManager());
            container.RegisterType<IGroupBranchRepository, GroupBranchRepository>(new TransientLifetimeManager());
            container.RegisterType<IOrderDetailRepository, OrderDetailRepository>(new TransientLifetimeManager());
            container.RegisterType<IRateParamTypeRepository, RateParamTypeRepository>(new TransientLifetimeManager());
            container.RegisterType<IRateRepository, RateRepository>(new TransientLifetimeManager());
            container.RegisterType<IRetailRateRepository, RetailRateRepository>(new TransientLifetimeManager());
            container.RegisterType<IGroupBranchCurrencyRepository, GroupBranchCurrencyRepository>(new TransientLifetimeManager());
            container.RegisterType<IBankAccountTypeRepository, BankAccountTypeRepository>(new TransientLifetimeManager());
            container.RegisterType<IBankRepository, BankRepository>(new TransientLifetimeManager());

            //Register application services mappings
            container.RegisterType<IAddressManagementService, AddressManagementService>(new TransientLifetimeManager());
            container.RegisterType<IBankAccountManagementService, BankAccountManagementService>(new TransientLifetimeManager());
            container.RegisterType<IBranchManagementService, BranchManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICityManagementService, CityManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICommonManagementService, CommonManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICountryManagementService, CountryManagementService>(new TransientLifetimeManager());
            container.RegisterType<IGroupBranchManagementService, GroupBranchManagementService>(new TransientLifetimeManager());
            container.RegisterType<IIdentityManagementService, IdentityManagementService>(new TransientLifetimeManager());
            container.RegisterType<IOrderManagementService, OrderManagementService>(new TransientLifetimeManager());
            container.RegisterType<IPhoneManagementService, PhoneManagementService>(new TransientLifetimeManager());
            container.RegisterType<ISoundexCodeManagementService, SoundexCodeManagementService>(new TransientLifetimeManager());
            container.RegisterType<IStateManagementService, StateManagementService>(new TransientLifetimeManager());
            container.RegisterType<IVirtualAccountManagementService, VirtualAccountManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICreditCardManagementService, CreditCardManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICustomerServiceManagementService, CustomerServiceManagementService>(new TransientLifetimeManager());
            container.RegisterType<IGroupBranchCurrencyManagementService, GroupBranchCurrencyManagementService>(new TransientLifetimeManager());
	        container.RegisterType<IOrderDetailManagementService, OrderDetailManagementService>(new TransientLifetimeManager());
            container.RegisterType<IRateParamTypeManagementService, RateParamTypeManagementService>(new TransientLifetimeManager());
            container.RegisterType<IRateManagementService, RateManagementService>(new TransientLifetimeManager());
            container.RegisterType<IRetailRateManagementService, RetailRateManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICustomerManagementService, CustomerManagementService>(new TransientLifetimeManager());
            container.RegisterType<IBankAccountManagementService, BankAccountManagementService>(new TransientLifetimeManager());
            container.RegisterType<ICustomerCompanyManagementService, CustomerCompanyManagementService>(new TransientLifetimeManager());
		   
            //Register domain services mappings


            //Register crosscuting mappings
            container.RegisterType<ITraceManager, TraceManager>(new TransientLifetimeManager());
            container.RegisterType<IAuditManager, IAuditManager>(new TransientLifetimeManager());
            container.RegisterType<IEmailManager, EmailManager>(new TransientLifetimeManager());
            container.RegisterType<ICacheManager, CacheMemoryManager>(new TransientLifetimeManager());
            container.RegisterType<IMembershipManager, MembershipManager>(new TransientLifetimeManager());
            container.RegisterType<IRoleManager, RoleManager>(new TransientLifetimeManager());
        }

        /// <summary>
        /// Configure real container. Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureRealContainer(IUnityContainer container)
        {
            container.RegisterType<IMainModuleUnitOfWork, MainModuleUnitOfWork>(new PerExecutionContextLifetimeManager(), new InjectionConstructor());
        }

        /// <summary>
        /// Configure fake container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureFakeContainer(IUnityContainer container)
        {
            //Note: Generic register type method cannot be used here, 
            //MainModuleFakeContext cannot have implicit conversion to IMainModuleContext

            container.RegisterType(typeof(IMainModuleUnitOfWork), typeof(FakeMainModuleUnitOfWork), new PerExecutionContextLifetimeManager());
        }

        #endregion

        #region IServiceFactory Members

        /// <summary>
        /// <see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve{TService}"/>
        /// </summary>
        /// <typeparam name="TService"><see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve{TService}"/></typeparam>
        /// <returns><see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve{TService}"/></returns>
        public TService Resolve<TService>()
        {
            //We use the default container specified in AppSettings
            string containerName = ConfigurationManager.AppSettings["defaultIoCContainer"];

            if (String.IsNullOrEmpty(containerName)
                ||
                String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException(Messages.exception_DefaultIOCSettings);
            }

            if (!_ContainersDictionary.ContainsKey(containerName))
                throw new InvalidOperationException(Messages.exception_ContainerNotFound);

            IUnityContainer container = _ContainersDictionary[containerName];

            return container.Resolve<TService>();
        }
        /// <summary>
        /// <see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve"/>
        /// </summary>
        /// <param name="type"><see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve"/></param>
        /// <returns><see cref="M:Infraestructure.CrossCutting.IoC.IContainer.Resolve"/></returns>
        public object Resolve(Type type)
        {
            //We use the default container specified in AppSettings
            string containerName = ConfigurationManager.AppSettings["defaultIoCContainer"];

            if (String.IsNullOrEmpty(containerName)
                ||
                String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException(Messages.exception_DefaultIOCSettings);
            }

            if (!_ContainersDictionary.ContainsKey(containerName))
                throw new InvalidOperationException(Messages.exception_ContainerNotFound);

            IUnityContainer container = _ContainersDictionary[containerName];

            return container.Resolve(type, null);
        }

        /// <summary>
        /// <see cref="M:Infraestructure.CrossCutting.IoC.IContainer.RegisterType"/>
        /// </summary>
        /// <param name="type"><see cref="M:Infraestructure.CrossCutting.IoC.IContainer.RegisterType"/></param>
        public void RegisterType(Type type)
        {
            IUnityContainer container = this._ContainersDictionary["RootContext"];

            if (container != null)
                container.RegisterType(type, new TransientLifetimeManager());
        }

        #endregion
    }
}
